/*
 * commons is a based project implemented
 * Copyright (C) 2024 Jasmine
 */
package com.commons.core.jackson;

import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.TimeZone;

import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;

import com.commons.core.domain.pojo.DatePattern;
import com.commons.core.utils.GeneralPropertySourceFactory;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;

/**
 * @author Jasmine
 * @version 1.0
 * @description
 * @see JacksonConfiguration
 * @since 2024-01-25 17:02:02
 */
@AutoConfiguration
@PropertySource(value = "classpath:default-json-jackson.yml", factory = GeneralPropertySourceFactory.class)
public class JacksonConfiguration {

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return builder -> {
            // 序列化时包含所有字段; 若想要NULL不参与序列化就设置 JsonInclude.Include.NON_NULL
            builder.serializationInclusion(JsonInclude.Include.ALWAYS);
            // 忽略无法转换的对象, 在序列化一个空对象时时不抛出异常
            builder.failOnEmptyBeans(false);
            // 忽略反序列化时在json字符串中存在, 但在java对象中不存在的属性; 忽略json字符串中不识别的属性
            builder.failOnUnknownProperties(false);

            // 指定时区 builder.timeZone("GMT+8:00")
            builder.timeZone(TimeZone.getDefault());

            builder.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
            builder.featuresToDisable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
            // builder.featuresToDisable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); // 忽略json字符串中不识别的属性
            // builder.featuresToDisable(SerializationFeature.FAIL_ON_EMPTY_BEANS); // 忽略无法转换的对象 空对象不让序列化
            // builder.featuresToEnable(SerializationFeature.INDENT_OUTPUT); // PrettyPrinter 格式化输出
            // builder.serializationInclusion(JsonInclude.Include.NON_NULL);//NULL不参与序列化

            // JSR 310日期时间处理 针对时间类型 的序列化和反序列化处理 支持JDK8 LocalDateTime、LocalDate、 LocalTime
            JavaTimeModule javaTimeModule = configJavaTimeModule();

            // 针对大数值的序列化处理, 解决序列化后传入前端Long类型精度丢失问题
            Module bigNumberModule = configBigNumberModule();

            builder.modules(javaTimeModule, bigNumberModule);

            builder.postConfigurer(obj -> obj.setSerializerProvider(new NullValueSerializerProvider()));
        };
    }

    /**
     * 针对大数值的序列化处理, 解决序列化后传入前端Long类型精度丢失问题
     */
    private SimpleModule configBigNumberModule() {
        SimpleModule bigNumberModule = new SimpleModule();
        bigNumberModule.addSerializer(Long.class, BigNumberSerializer.SERIALIZER_INSTANCE);
        bigNumberModule.addSerializer(Long.TYPE, BigNumberSerializer.SERIALIZER_INSTANCE);
        bigNumberModule.addSerializer(BigInteger.class, BigNumberSerializer.SERIALIZER_INSTANCE);
        return bigNumberModule;
    }

    /**
     * 初始化JavaTimeModule 支持JDK8 LocalDateTime、LocalDate、 LocalTime
     */
    private JavaTimeModule configJavaTimeModule() {
        JavaTimeModule javaTimeModule = new JavaTimeModule();

        // 处理LocalDateTime
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN);
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(dateTimeFormatter));
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(dateTimeFormatter));

        // 处理LocalDate 默认日期格式 yyyy-MM-dd
        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN);
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(dateFormatter));
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(dateFormatter));

        // 处理LocalTime 默认时间格式 HH:mm:ss
        DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern(DatePattern.NORM_TIME_PATTERN);
        javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(timeFormatter));
        javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(timeFormatter));

        // Date序列化和反序列化
        // javaTimeModule.addSerializer(Date.class, new DateSerializer());
        // javaTimeModule.addDeserializer(Date.class, new DateDeserializers.DateDeserializer());

        return javaTimeModule;
    }

}
